#ifndef __CONFIG_H__
#define __CONFIG_H__

#include <memory>
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <yaml-cpp/yaml.h>
#include <yaml-cpp/node/node.h>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <iostream>
#include <functional>

#include "log.h"
#include "thread.h"
#include "lock.h"

namespace johnsonli
{
	//配置基本信息类
	class ConfigVarBase
	{
	public:
		typedef std::shared_ptr<ConfigVarBase> ptr;
		ConfigVarBase(const std::string& name, const std::string& description = "")
			:m_name(name)
			,m_description(description)
		{
			//把名字都转换成小写
			std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);
		}

		virtual ~ConfigVarBase() {}
		
		const std::string& getName() const { return m_name; }
		const std::string& getDescription() const { return m_description; }

		virtual std::string toString() = 0;
		virtual bool fromString(const std::string& val) = 0;
		virtual std::string getTypeName() const = 0;
	protected:
		std::string m_name;
		std::string m_description;
	};

	/**
	 * @brief 类型转换模板类(F 源类型, T 目标类型)
	 */
	template<class F, class T>
	class LexicalCast {
	public:
		/**
		 * @brief 类型转换
		 * @param[in] v 源类型值
		 * @return 返回v转换后的目标类型
		 * @exception 当类型不可转换时抛出异常
		 */
		T operator()(const F& v) {
			return boost::lexical_cast<T>(v);
		}
	};

	/**
	 * @brief 类型转换模板类片特化(YAML String 转换成 std::vector<T>)
	 */
	template<class T>
	class LexicalCast<std::string, std::vector<T> > {
	public:
		std::vector<T> operator()(const std::string& v) {
			//"yaml string [1,2,3] 转换成yaml node"
			YAML::Node node = YAML::Load(v);
			typename std::vector<T> vec;
			std::stringstream ss;
			for (size_t i = 0; i < node.size(); ++i) {
				ss.str("");
				ss << node[i];
				vec.push_back(LexicalCast<std::string, T>()(ss.str()));
			}
			return vec;
		}
	};

	/**
	 * @brief 类型转换模板类片特化(std::vector<T> 转换成 YAML String)
	 */
	template<class T>
	class LexicalCast<std::vector<T>, std::string> {
	public:
		std::string operator()(const std::vector<T>& v) {
			YAML::Node node(YAML::NodeType::Sequence);
			for (auto& i : v) {
				node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
			}
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};

	/**
	 * @brief 类型转换模板类片特化(YAML String 转换成 std::list<T>)
	 */
	template<class T>
	class LexicalCast<std::string, std::list<T> > {
	public:
		std::list<T> operator()(const std::string& v) {
			YAML::Node node = YAML::Load(v);
			typename std::list<T> vec;
			std::stringstream ss;
			for (size_t i = 0; i < node.size(); ++i) {
				ss.str("");
				ss << node[i];
				vec.push_back(LexicalCast<std::string, T>()(ss.str()));
			}
			return vec;
		}
	};

	/**
	 * @brief 类型转换模板类片特化(std::list<T> 转换成 YAML String)
	 */
	template<class T>
	class LexicalCast<std::list<T>, std::string> {
	public:
		std::string operator()(const std::list<T>& v) {
			YAML::Node node(YAML::NodeType::Sequence);
			for (auto& i : v) {
				node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
			}
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};

	/**
	 * @brief 类型转换模板类片特化(YAML String 转换成 std::set<T>)
	 */
	template<class T>
	class LexicalCast<std::string, std::set<T> > {
	public:
		std::set<T> operator()(const std::string& v) {
			YAML::Node node = YAML::Load(v);
			typename std::set<T> vec;
			std::stringstream ss;
			for (size_t i = 0; i < node.size(); ++i) {
				ss.str("");
				ss << node[i];
				vec.insert(LexicalCast<std::string, T>()(ss.str()));
			}
			return vec;
		}
	};

	/**
	 * @brief 类型转换模板类片特化(std::set<T> 转换成 YAML String)
	 */
	template<class T>
	class LexicalCast<std::set<T>, std::string> {
	public:
		std::string operator()(const std::set<T>& v) {
			YAML::Node node(YAML::NodeType::Sequence);
			for (auto& i : v) {
				node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
			}
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};

	/**
	 * @brief 类型转换模板类片特化(YAML String 转换成 std::unordered_set<T>)
	 */
	template<class T>
	class LexicalCast<std::string, std::unordered_set<T> > {
	public:
		std::unordered_set<T> operator()(const std::string& v) {
			YAML::Node node = YAML::Load(v);
			typename std::unordered_set<T> vec;
			std::stringstream ss;
			for (size_t i = 0; i < node.size(); ++i) {
				ss.str("");
				ss << node[i];
				vec.insert(LexicalCast<std::string, T>()(ss.str()));
			}
			return vec;
		}
	};

	/**
	 * @brief 类型转换模板类片特化(std::unordered_set<T> 转换成 YAML String)
	 */
	template<class T>
	class LexicalCast<std::unordered_set<T>, std::string> {
	public:
		std::string operator()(const std::unordered_set<T>& v) {
			YAML::Node node(YAML::NodeType::Sequence);
			for (auto& i : v) {
				node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
			}
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};

	/**
	 * @brief 类型转换模板类片特化(YAML String 转换成 std::map<std::string, T>)
	 */
	template<class T>
	class LexicalCast<std::string, std::map<std::string, T> > {
	public:
		std::map<std::string, T> operator()(const std::string& v) {
			YAML::Node node = YAML::Load(v);
			typename std::map<std::string, T> vec;
			std::stringstream ss;
			for (auto it = node.begin();
				it != node.end(); ++it) {
				ss.str("");
				ss << it->second;
				vec.insert(std::make_pair(it->first.Scalar(),
					LexicalCast<std::string, T>()(ss.str())));
			}
			return vec;
		}
	};

	/**
	 * @brief 类型转换模板类片特化(std::map<std::string, T> 转换成 YAML String)
	 */
	template<class T>
	class LexicalCast<std::map<std::string, T>, std::string> {
	public:
		std::string operator()(const std::map<std::string, T>& v) {
			YAML::Node node(YAML::NodeType::Map);
			for (auto& i : v) {
				node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
			}
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};

	/**
	 * @brief 类型转换模板类片特化(YAML String 转换成 std::unordered_map<std::string, T>)
	 */
	template<class T>
	class LexicalCast<std::string, std::unordered_map<std::string, T> > {
	public:
		std::unordered_map<std::string, T> operator()(const std::string& v) {
			YAML::Node node = YAML::Load(v);
			typename std::unordered_map<std::string, T> vec;
			std::stringstream ss;
			for (auto it = node.begin();
				it != node.end(); ++it) {
				ss.str("");
				ss << it->second;
				vec.insert(std::make_pair(it->first.Scalar(),
					LexicalCast<std::string, T>()(ss.str())));
			}
			return vec;
		}
	};

	/**
	 * @brief 类型转换模板类片特化(std::unordered_map<std::string, T> 转换成 YAML String)
	 */
	template<class T>
	class LexicalCast<std::unordered_map<std::string, T>, std::string> {
	public:
		std::string operator()(const std::unordered_map<std::string, T>& v) {
			YAML::Node node(YAML::NodeType::Map);
			for (auto& i : v) {
				node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
			}
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};



	//配置变量类
	//FromStr T operator()(const string&)
	//ToStr std::string operator()(const T&)
	template<class T, class FromStr = LexicalCast<std::string, T>
					, class ToStr = LexicalCast<T, std::string>>
	class ConfigVar : public ConfigVarBase
	{
	public:
		typedef RWMutex RWMutexType;
		typedef std::shared_ptr<ConfigVar> ptr;
		typedef std::function<void (const T& old_value, const T& new_value)> on_change_cb;

		ConfigVar(const std::string& name, const T& default_val, const std::string& description)
			:ConfigVarBase(name, description)
			,m_val(default_val)
		{}

		//T to string YAML string
		std::string toString() override
		{
			try
			{
				//return boost::lexical_cast<std::string>(m_val);
				RWMutexType::ReadLock lock(m_mutex);
				return  ToStr()(m_val);
			}
			catch (std::exception& e)
			{
				LOG_ERROR(LOG_ROOT()) << "ConfigVar::toString exception"
					<< e.what() << " convert: " << typeid(m_val).name() << " to string";
			}

			return "";

		}

		//YAML string to T
		bool fromString(const std::string& val) override
		{
			try
			{
				//boost::lexical_cast<T>(val)
				//RWMutexType::ReadLock lock(m_mutex);
				setValue(FromStr()(val));
				return true;
			}
			catch (std::exception& e)
			{
				LOG_ERROR(LOG_ROOT()) << "ConfigVar::fromString exception"
					<< e.what() << " convert: string to" << typeid(m_val).name();
			}

			return false;
		}

		T getValue() { 
			RWMutexType::ReadLock lock(m_mutex);
			return m_val; 
		}
		void setValue(const T& value) 
		{
			{
				RWMutexType::ReadLock lock(m_mutex);
				//std::cout << "setvalue" << std::endl;
				if (value == m_val)
				{
					return;
				}
				//只有当old 和 new不同时才回调
				for (auto& i : m_cbs)
				{
					i.second(m_val, value);
				}
			}
			RWMutexType::WriteLock lock(m_mutex);
			m_val = value; 

			//std::cout << "setValue end" << std::endl;
		}

		std::string getTypeName() const override
		{
			return typeid(T).name();
		}

		uint64_t addListener(on_change_cb cb)
		{
			//每次由系统决定key值，再返回，用于delListener
			static uint64_t s_idx = 0;
			RWMutexType::WriteLock lock(m_mutex);	
			++s_idx;
			m_cbs[s_idx] = cb;
			return s_idx;
		}

		void delListener(uint64_t key)
		{
			RWMutexType::WriteLock lock(m_mutex);
			m_cbs.erase(key);
		}

		on_change_cb getListener(uint64_t key)
		{
			RWMutexType::ReadLock lock(m_mutex);
			auto it = m_cbs.find(key);
			return it == m_cbs.end() ? nullptr : it->second;
		}

		//清空监听器
		void clearListener()
		{
			RWMutexType::WriteLock lock(m_mutex);
			m_cbs.clear();
		}

	private:
		T m_val;
		// 回调函数，当配置有改变时，回调
		std::map<uint64_t, on_change_cb> m_cbs;
		RWMutexType m_mutex;
	};

	//管理类
	class Config
	{
	public:
		typedef RWMutex RWMutexType;
		typedef std::map<std::string, ConfigVarBase::ptr> ConfigVarMap;

		//查找一个配置变量，没找到就创建一个新的，放到s_datas
		//name：A.B
		template<class T>
		static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "")
		{
			RWMutexType::WriteLock lock(GetMutex());
			auto tmp = Lookup<T>(name);
			//已经存在
			if (tmp)
			{
				return tmp;
			}

			//名字只能是大小写字母，数字，.，_
			if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyz._0123456789")
				!= std::string::npos)
			{
				LOG_ERROR(LOG_ROOT()) << "Lookup name invalid " << name;
				throw std::invalid_argument(name);
			}

			typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description));
			GetDatas()[name] = v;
			return v;
		}

		template<class T>
		static typename ConfigVar<T>::ptr Lookup(const std::string& name)
		{
			auto it = GetDatas().find(name);
			if (it != GetDatas().end())
			{
				auto tmp =std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
				if (tmp)
				{
					LOG_INFO(LOG_ROOT()) << "Lookup name=" << name << " exists";
					return tmp;
				}
				else //key一样，value类型不匹配
				{
					LOG_ERROR(LOG_ROOT()) << "Lookup name=" << name << " exists but type not same "
						<< typeid(T).name() << " real_type " << it->second->getTypeName() << " " << it->second->toString();
					return nullptr;
				}
			}
			
			return nullptr;
		}
		


		//解析yaml文件，读取配置信息
		static void LoadFromYaml(const YAML::Node& root);

		/**
		 * @brief 加载path文件夹里面的配置文件
		 */
		static void LoadFromConfDir(const std::string& path, bool force = false);
		
		//查看当前配置表的信息
		static void Visit(std::function<void(johnsonli::ConfigVarBase::ptr)> cb)
		{
			ConfigVarMap& m = GetDatas();
			std::cout << "m size: " << m.size() << std::endl;
			for (auto it = m.begin(); it != m.end(); ++it)
			{
				cb(it->second);
			}
		}
	
	private:
		static ConfigVarBase::ptr LookupBase(const std::string& name);
		
	private:
		/**
		 * @brief 返回所有的配置项
		*/
		static ConfigVarMap& GetDatas() {
			static ConfigVarMap s_datas;
			return s_datas;
		}
		
		//如果是静态成员变量，可能对象会先创建出来，但是静态变量还没有初始化
		static RWMutexType& GetMutex()
		{
			static RWMutexType s_mutex;
			return s_mutex;
		}

	};
}


#endif
